﻿using ArtMath.Core.Data;
using ArtMath.Core.Helper;
using ArtMath.PointCollection.Helper;
using ArtMath.Setting;
using System;
using System.Collections.Generic;

namespace ArtMath.PointCollection
{
    [Serializable]
    public class CurveDescribable : ContinuityPoint
    {
        public IList<Curve> Curves;
        public override PointD? HeadPoint
        {
            get
            {
                if (!Curves[0].IsClosed)
                {
                    return Curves[0][0];
                }
                return base.HeadPoint;
            }
        }
        public override PointD? TailPoint
        {
            get
            {
                Curve last = Curves[Curves.Count - 1];
                if (!last.IsClosed)
                {
                    return last[last.Count - 1];
                }
                return base.TailPoint;
            }
        }
        public override bool Contains(PointD pt)
        {
            if (Curves == null || !AfterContains(pt)) return false;
            foreach (Curve item in Curves)
            {
                if (item.Contains(pt))
                {
                    return true;
                }
            }
            return false;
        }
        public override PointD? GetNearestPoint(PointD pt)
        {
            if (Curves == null) return null;
            return Curves.GetNearestPointFromCurves(pt);
        }
        public LengthCurve CurrentCurve;
        public override double GetSlideExcess(PointD currentLocation, double length)
        {
            Curves.GetCurveIndexAndPointIndex(currentLocation, out int curveIndex, out int pointIndex);
            CurrentCurve = Curves[curveIndex] as LengthCurve;
            if (CurrentCurve != null && !CurrentCurve.IsClosed)
            {
                double d = currentLocation.DistanceTo(CurrentCurve[pointIndex]), sum = 0;
                if (length > 0)
                {
                    if (!Settings.Instance.AnimateOnOneCurve)
                    {
                        for (int i = curveIndex + 1; i < Curves.Count; i++)
                        {
                            LengthCurve lc = Curves[i] as LengthCurve;
                            if (lc == null) continue;
                            sum += lc.Length;
                        }
                    }
                    return length + d - CurrentCurve.GetToEndLength(pointIndex) - sum;
                }
                else
                {
                    if (!Settings.Instance.AnimateOnOneCurve)
                    {
                        for (int i = 0; i < curveIndex; i++)
                        {
                            LengthCurve lc = Curves[i] as LengthCurve;
                            if (lc == null) continue;
                            sum += lc.Length;
                        }
                    }
                    return -length - d - CurrentCurve.GetFromStartLength(pointIndex) - sum;
                }
            }
            return base.GetSlideExcess(currentLocation, length);
        }
        public override PointD SlideDestination(PointD currentLocation, double length, out bool reverse)
        {
            reverse = false;
            if (length == 0) return currentLocation;
            Curves.GetCurveIndexAndPointIndex(currentLocation, out int curveIndex, out int pointIndex);
            length += currentLocation.DistanceTo(Curves[curveIndex][pointIndex]);
            return SlideDestination(curveIndex, pointIndex, length, out reverse);
        }
        public PointD SlideDestination(int currentCurveIndex, int currentPointIndex, double length, out bool reverse)
        {
            LengthCurve cur;
            if (Curves.Count == 1)
            {
                cur = Curves[0] as LengthCurve;
                if (cur != null)
                {
                    return cur.SlideDestination(currentPointIndex, length, out reverse);
                }
            }
            //reverse = false;
            //if (length == 0) return Curves[currentCurveIndex].Samples[currentPointIndex];
            cur = new LengthCurve();
            int allIndex = 0;
            SizeD[] offsets = new SizeD[Curves.Count];
            for (int i = 0; i < Curves.Count; i++)
            {
                if (i == currentCurveIndex)
                {
                    allIndex = cur.Count + currentPointIndex - i;
                }
                if (i == 0)
                {
                    offsets[i] = new SizeD(0, 0);
                    cur.AddRange(Curves[i]);
                }
                else
                {
                    PointD newloc = Curves[i - 1][Curves[i - 1].Count - 1], oldloc = Curves[i][0];
                    offsets[i] = offsets[i - 1] + new SizeD(newloc.X - oldloc.X, newloc.Y - oldloc.Y);
                    for (int j = 1; j < Curves[i].Count; j++)
                    {
                        cur.Add(Curves[i][j] + offsets[i]);
                    }
                }
            }
            PointD res = cur.SlideDestination(allIndex, length, out reverse);
            int index = cur.GetIndex(res);
            for (int i = 0; i < Curves.Count; i++)
            {
                index -= Curves[i].Count - 1;
                if (index < 0 || index == 0 && i == Curves.Count - 1)
                {
                    return res - offsets[i];
                }
            }
            return res;
        }
        protected override PointCollectionBase Intersect(PointCollectionBase collection)
        {
            HashSet<PointD> set = new HashSet<PointD>();
            CurveDescribable cd = collection as CurveDescribable;
            if (cd != null && cd.Curves != null)
            {
                foreach (Curve cur1 in Curves)
                {
                    foreach (Curve cur2 in cd.Curves)
                    {
                        set.AddRange(cur1.CurvesIntersection(cur2));
                    }
                }
            }
            if (set.Count == 0) return Empty.Instance;
            return AfterIntersect(collection.AfterIntersect(new DiscontinuityPoint(set)));
        }
    }
}
